/*****************************************************************************
 * (C) Copyright 2013 Atech-Automotive
 * FILE NAME:    diag.c
 * DESCRIPTION:  Check inputs and outputs to detect failures.
 * DATE BEGUN:   May 2013
 * BY:           Allen Feng
 * PRODUCT NAME: ZOYTE B11 BCM
 * APPLICATION:  
 * TARGET H/W:   
 * DOC REF:
 *****************************************************************************
 */

#define diag_c

/*****************************************************************************
 *  I D E N T                                                                *
 *****************************************************************************/
#ifdef USE_IDENT
    #ident "$Id: diagnostics.c,v 1.7 2008/04/22 03:24:02 AndrewBeal Exp $"
#endif

/* #include */
#include "sys_micro.h"
#include "system.h"
#include "diag.h"
#include "diag_cfg.h"
#include "diag_itf.h"
#include "diag_fun.h"	
//#include "hwa_a2dActions.h"
#include "hwa_outputs.h"
#include "hwa_inputs_cfg.h"
#include "hwa_inputs.h"
#include "hwa_e2_Cfg.h"
#include "driver_e2.h"
#include "hwa_e2.h"
#include "hwa_e2_callback.h"
/* #define */

//	/* typedef */

static Bit F_readDTCE2=1;
//	/* variables holding error status (amongst others) */
static DTCE2State dtcE2State = DTC_E2_IDLE;

//	/* static constants */
//	/* static function prototypes */

static Byte dtcE2Status;
static Bit  F_prohibitDTCStore;

/****************************************************************************/
/**
 * Function Name: void ioctlDiagnostics(Byte cmd)
 * Description:   IOCTL function
 *
 * Param:   cmd - IOCTL command
 * Return:  none
 * Author:  rrosenba
 *****************************************************************************/
void ioctlDiagnostics(Byte cmd)
{
	if (cmd ==IOCTL_DTC_STOP)
	{
		F_prohibitDTCStore = SET;
		clearDTCs();
	}
	else if (cmd == IOCTL_DTC_START)
	{
		F_prohibitDTCStore = CLEAR;
	}
	else
	{
		//do nothing
	}
}
/*****************************************************************************/
/**
 * Function Name: Byte getActiveDTCs(Byte cmd, Byte *pDstBuf)
 * Description:   Copy active DTCs (offsets to LUT) to destination buffer and
 *                return number of active Diagnostic Trouble Codes.
 *
 * Param:   Byte - command
 *          Byte - Pointer to destination where DTCs are copied to
 * Return:  Byte - Number of active DTCs (copied to dest. buffer)
 * Author:  rrosenba
 *****************************************************************************/
Byte getActiveDTCs(Byte cmd, Byte *pDstBuf)
{
	Byte i;
	Byte num;
	num = 0;
	if(cmd == HISTORY_DTC_MASK)
	{
		(void)memcpy(pDstBuf,histDTCBuf.dtcBuf,MAX_DTCS_STORED);
		for ( i = 0; i < MAX_DTCS_STORED; i++)
		{
			if (histDTCBuf.dtcBuf[i]== 0xFF)//empty cell
			{
				return i;//number of valid history DTCs
//					break;
			}
		}
		return MAX_DTCS_STORED;
	}
	else if (cmd == CURRENT_DTC_MASK)
	{
		for ( i = 0; i < NUM_OF_DTCS; i++)
		{
			if (dtcStatus[i].enable == FALSE)
			{
				//do nothing
			}
			else if (dtcStatus[i].status & FAIL_CONFIR_BIT)	//search for current active DTCs
			{
				*pDstBuf++ = i;	//store DTC 
				num++;
			}
			if (num >= MAX_DTCS_STORED)
			{
				break;		// only care about 11 DTCs
			}
			
		}
		return num;
	}
	else if (cmd == CHK_CUR_FAIL_STS)
	{
		for ( i = 0; i < MAX_DTCS_STORED; i++)
		{
			if (histDTCBuf.dtcBuf[i]== 0xFF)//empty cell
			{
				num = i;//number of valid history DTCs
				break;
			}
			else if (i == NUM_OF_DTCS-1)//all cells are occupied
			{
				num = NUM_OF_DTCS;//number of valid history DTCs
			}
		}
		return num;
	}

}
/*****************************************************************************/
/**
 * Function Name: void clearDTCs(void)
 * Description:   Clear all the DTC stored in RAM and set a flag
 *               	     indicating ready to clear EEPROM data
 *
 * Param:   
 *         
 * Return:  
 * Author:  af
 *****************************************************************************/
 void clearDTCs(void)
 {
	Byte i;
 
 	for ( i = 0; i < NUM_OF_DTCS; i++)
	{
		dtcStatus[i].status = 0;	//clear all DTC status
	}
	 
	 for ( i = 0; i < MAX_DTCS_STORED; i++)
	 {
		 histDTCBuf.dtcBuf[i] = 0xFF;	// clear all historical DTCs
	 }
	 histDTCBuf.head = 0;

	 F_histDTCUpdated = SET;	//use this flag to indicate ready to update EEPROM
	 clearDiagnosticsStatus();
 }
	

/*****************************************************************************/
/**
 * Function Name: void e2RWDTCs(void)
 * Description:   
 *               	    
 *
 * Param:   
 *         
 * Return:  
 * Author:  af
 *****************************************************************************/
 void e2RWDTCs(void) 
 {
    UINT8 i;
    UINT8 r_state=0;
    UINT32 e2_dtc_data0=0;
    (void)memcpy(&e2_dtc_data0,&histDTCBuf.dtcBuf[0],4);
    switch(dtcE2State)
    {
      case DTC_E2_IDLE:
          if(F_readDTCE2) 
          {
              (void)hwa_e2ReadDataById(E2_ADDR_DTC0,&histDTCBuf.dtcBuf[0]);
             // (void)memcpy(&histDTCBuf.dtcBuf[0],&e2_dtc_data0,4);
              F_readDTCE2 = CLEAR;
          } 
          else if(F_histDTCUpdated) 
          {   
              r_state = hwa_e2WriteDataById(E2_ADDR_DTC0,e2_dtc_data0);
              if(r_state) F_histDTCUpdated = CLEAR;
          }
      break;
    }
 }
 
/*****************************************************************************/
/**
 * Function Name: void diagHandler(void)
 * Description:   
 *
 * Param:   none
 * Return:  none
 * Author:  rrosenba
 *****************************************************************************/
void diagHandler(void)
{
    	Byte i;
    	static Byte timer = 0;
		e2RWDTCs();

	if (F_prohibitDTCStore)//stop updating DTC status
	{
		return;
	}

	checkDiags();

	if (timer < 100)	// 1second delay
	{
		timer++;
		return;
	}
	else
	{
		timer = 0;
	}
	configDiags();
	
//		if (F_histDTCUpdated == FALSE)// do not update diag when E2 is not finished
//		{
		Diag_Interface();//update interfaces
	
		for (i=0; i<NUM_OF_DTCS;i++)
		{
//			if (dtcStatus[i].enable == TRUE)	//only update enabled DTCs
			{
				updateDTC(dtcStatus[i].diag_In,i);	//update DTC codes
			}
		}
//		}
}

/*****************************************************************************/
/**
 * Function Name: void updateHistDTCBuf(Byte dtcSeq)
 * Description:   update historical DTC buffer, FIFO

 * Param:  none 
 * Return:  none
 * Author:  A.Feng
 *****************************************************************************/	
void updateHistDTCBuf(Byte dtcSeq)
 {
	Byte i;

	if (F_prohibitDTCStore)
	{
		return;
	}
	
	for  (i = 0; i < MAX_DTCS_STORED; i++)
	{
		if (histDTCBuf.dtcBuf[i] == dtcSeq)
		{
			return;	//the dtc already in the hist buffer, no need to rewrite
		}
	}
	if (histDTCBuf.head ==MAX_DTCS_STORED)	//buffer full 
	{
		histDTCBuf.head = 0;	//write buffer from the beginning 
	}
	histDTCBuf.dtcBuf[histDTCBuf.head] = dtcSeq;
	histDTCBuf.head++;
	F_histDTCUpdated = SET;
	//this flag is to update historical DTC buffer in EEPRom
 }
/*****************************************************************************/
/**
 * Function Name: Byte getNumOfSupportedDTCs()
 * Description:   
 *
 * Param:   none
 * Return:  number of supported DTCs defined in the lookup table
 * Author:  roland
 *****************************************************************************/
Byte getNumOfSupportedDTCs()
{
	Byte tmp;
	tmp = NUM_OF_DTCS;
	return  tmp;//sizeof(dtcLUT) / sizeof(dtcLUT[0]);
}
//	
/*****************************************************************************/
/**
 * Function Name: void iniDiagnostics(void)
 * Description:   
 *
 * Param:   none
 * Return:  none
 * Author:  rrosenba
 *****************************************************************************/
void iniDiagnostics(void)
{
	Byte i;
	for (i = 0; i<NUM_OF_DTCS; i++)
	{
		dtcStatus[i].enable = TRUE;//first set all dtc to be enabled
		dtcStatus[i].status = 0;//ini dtc values
	}
	for (i = 0; i<MAX_DTCS_STORED; i++)
	{
		histDTCBuf.dtcBuf[i] = 0xFF;	//ini dtc values
	}

	histDTCBuf.head = 0;
	F_histDTCUpdated = CLEAR;
	clearDiagnosticsStatus();
	iniDiagStatus();	//ini variables of dtc structures
	

}



void updateDTC(Byte cmd, Byte id)   //this api is used to update DTC
{
	if (F_prohibitDTCStore)
	{
		return;
	}

	if (dtcStatus[id].enable == FALSE)
	{
		return;
	}

	if (cmd == SET)
	{
		if ((dtcStatus[id].status &FAIL_CONFIR_BIT) == CLEAR)//new DTC
		{
				updateHistDTCBuf(id);
		}
		dtcStatus[id].status |= DTC_CONFIR_MASK;//?  ZHI JIE == 9?
		
	}
	else
	{
		dtcStatus[id].status &= (~FAIL_BIT);//clear DTC
	}
}


 
/*****************************************************************************/
/**
 * Function Name: void checkLSD(LSDDiagInfo *diag)
 * Description:  check LSD diag
 *			Detectable Errors:
 *			- LSD Short to Bat
 *			- LSD Open Load
 * Param:   LSDDiagInfo *diag
 * Return:  none
 * Author:  AF
 * *****************************************************************************/
 void checkLSD(LSDDiagInfo *diag, Byte diagIn,Byte outputStatus, Byte* overCurrentStatus)
 {
	diag->F_outp = outputStatus;
	if(diag->F_outp != diag->F_prevOutp)
	{
		diag->F_prevOutp   = diag->F_outp;
		diag->delayCounter = 0;
	}
	if(diag->delayCounter < diag->delayTime)
	{
	        diag->delayCounter++;
	}
	else if (!diag->F_outp)		//output off	
	{
		if (diagIn == OFF)// detect low during output off
		{
			diag->errStatus |= LSD_OPEN_LOAD_ERR;
		}
		else
		{
			diag->errStatus &= (Byte)~LSD_OPEN_LOAD_ERR;
		}

	}
	else if (diag->F_outp)		//output on
	{
		if (diagIn == ON)// detect high during output on
		{
			diag->errStatus |= LSD_BAT_ERR;
			*overCurrentStatus = TRUE;	//inform hwa_outputs module of short to BAT
		}
		else
		{
			diag->errStatus &= (Byte)~LSD_BAT_ERR;
		}
	}
 }

 void checkLSD_PWM(LSDDiagInfo *diag, Byte diagIn,Byte outputStatus, Byte* overCurrentStatus)
  {
     diag->F_outp = outputStatus;
     if(diag->F_outp != diag->F_prevOutp)
     {
         diag->F_prevOutp   = diag->F_outp;
         diag->delayCounter = 0;
     }
     if(diag->delayCounter < diag->delayTime)
     {
             diag->delayCounter++;
     }
     else if (outputStatus == 0x02)     //output off    
     {
         if (diagIn == OFF)// detect low during output off
         {
             diag->errStatus |= LSD_OPEN_LOAD_ERR;
         }
         else
         {
             diag->errStatus &= (Byte)~LSD_OPEN_LOAD_ERR;
         }
     }
     else if (outputStatus == 0x03)      //output on
     {
         if (diagIn == ON)// detect high during output on
         {
             diag->errStatus |= LSD_BAT_ERR;
             *overCurrentStatus = TRUE;  //inform hwa_outputs module of short to BAT
         }
         else
         {
             diag->errStatus &= (Byte)~LSD_BAT_ERR;
         }
     }
  }

 



/*****************************************************************************/
/**
 * Function Name: void checkHSDs(HSDDiagInfo *diag)
 * Description:   check HSD diag
 *                Detectable Errors:
 *                - HSD Current Low
 *                - HSD Current High
 * Param:  none 
 * Return:  none
 * Author:  A.Feng
 *****************************************************************************/
 void checkHSD(HSDDiagInfo* diag,Byte diagData,Byte outputStatus,Byte* overCurrentStatus)
 {
	 /* clear delay counter if output has changed */
	 Byte temp;
	 diag->F_outp = outputStatus;
	 
	 if(diag->F_outp != diag->F_prevOutp)
	 {
		diag->F_prevOutp   = diag->F_outp;
		diag->delayCounter = 0;
		diag->a2dIdx = 0;
		diag->a2dBuffer[0] = 0;
		diag->a2dBuffer[1] = 0;
		diag->a2dBuffer[2] = 0;
		diag->a2dBuffer[3] = 0;
		diag->a2dBuffer[4] = 0;
	 }
     if (diag->F_outp == FALSE)
     {
         return; //only diag when ON
     }

	diag->a2dBuffer[diag->a2dIdx] = diagData;	//fill in diag A2D data
	if (diag->a2dIdx <4)
	{
		diag->a2dIdx++;
	}
	else
	{
		diag->a2dIdx = 0;
	}

	if(diag->delayCounter < diag->delayTime)
	{
	    diag->delayCounter++;
	}
	else
	{
		temp = hwa_A2dActionsFilter(A2D_AVG_SIZE, diag->a2dBuffer);
		if (temp < diag->lowTh)
		{
			diag->errStatus = HSD_LO_I_ERR;
		}
		else if (temp > diag->highTh)
		{
			diag->errStatus = HSD_HI_I_ERR;
//			if (outputStatus)
//			{
				*overCurrentStatus = TRUE;	//inform hwa_outputs module to switch off
//			}
		}
		else
		{
			diag->errStatus = HSD_NO_ERR;
		}
		
	}

 }


 void check_VBAT(VBATDiagInfo* diag,Byte diagData)
{
	 /* clear delay counter if output has changed */
	Byte temp;

	diag->a2dBuffer[diag->a2dIdx] = diagData;	//fill in diag A2D data
	if (diag->a2dIdx <4)
	{
		diag->a2dIdx++;
	}
	else
	{  
		   temp = hwa_A2dActionsFilter(A2D_AVG_SIZE, diag->a2dBuffer);
	       //if (temp < VBAT_8_5V)
	       if (temp < VBAT_8_5V)
	       {
				diag->ConfirmCounterH=0;
				diag->ConfirmCounterNOR=0;
				diag->errStatusL |= DIAG_VBAT_L_CURR_ERR;
			    if(diag->ConfirmCounterL < diag->ConfirmTimesL)
				{
				        diag->ConfirmCounterL++;
				}else{
						diag->ConfirmCounterL=0;
						diag->errStatusL |= DIAG_VBAT_L_CONFIRM_ERR;
				}
			   
	       }
	       else if (temp > VBAT_16_5V)
	       {
			   diag->ConfirmCounterL=0;
			   diag->ConfirmCounterNOR=0;
			   diag->errStatusH |= DIAG_VBAT_H_CURR_ERR;
			   if(diag->ConfirmCounterH < diag->ConfirmTimesH)
			   {
				  		diag->ConfirmCounterH++;
			   }else{
						diag->ConfirmCounterH=0;
						diag->errStatusH |= DIAG_VBAT_H_CONFIRM_ERR;
			   }
	       }
	       else if((temp > VBAT_9V) && (temp < VBAT_16V))
	       {
			   diag->ConfirmCounterL=0;
			   diag->ConfirmCounterH=0;
			   diag->errStatusL &= (~DIAG_VBAT_L_CURR_ERR);
			   diag->errStatusH &= (~DIAG_VBAT_H_CURR_ERR);
			   if(diag->ConfirmCounterNOR < diag->ConfirmTimesNOR)
			   {
				  		diag->ConfirmCounterNOR++;
			   }else{
						diag->ConfirmCounterNOR=0;
						diag->errStatusH &= (~DIAG_VBAT_H_CONFIRM_ERR);
						diag->errStatusL &= (~DIAG_VBAT_L_CONFIRM_ERR);
			   }
			   
       	   }
		diag->a2dIdx = 0;
	}

}

 void check_Temprature(TemperDiagInfo* diag ,Byte diagData)
 {	 Byte temp;
	 diag->a2dBuffer[diag->a2dIdx] = diagData;	 //fill in diag A2D data
	 if (diag->a2dIdx <4)
	 {
		if((diag->delayTimeCounter) > 20 ){
		  diag->a2dIdx++;
		  diag->delayTimeCounter = 0;
		}else{
		  diag->delayTimeCounter++;
		}
	 }
	 else
	 {	
			temp = hwa_A2dActionsFilter(A2D_AVG_SIZE, diag->a2dBuffer);
			if ((temp < DIAG_TemperatureSensor_0_1V) || (temp > DIAG_TemperatureSensor_4_9V) )
			{
				 diag->ConfirmCounterNOR=0;
				 diag->errStatus |= 1;
				 if(diag->ConfirmCounterErr < diag->ConfirmTimesErr)
				 {
						 diag->ConfirmCounterErr++;
				 }else{
						 diag->ConfirmCounterErr=0;
						 diag->errStatus |= 8;
				 }
				
			}
			else if((temp > DIAG_TemperatureSensor_0_1V) && (temp < DIAG_TemperatureSensor_4_9V))
			{
				diag->ConfirmCounterErr=0;
				diag->errStatus &= (~1);
				if(diag->ConfirmCounterNOR < diag->ConfirmTimesNOR)
				{
						 diag->ConfirmCounterNOR++;
				}else{
						 diag->ConfirmCounterNOR=0;
						 diag->errStatus &= (~8);
				}
				
			}
		 diag->a2dIdx = 0;
	 }

 }
 /*****************************************************************************/
 /**
  * Function Name: void iniHSDDiags(HSDDiagInfo *diag)
  * Description:   ini HSD diag
  * 			   Detectable Errors:
  * 			   - HSD Current Low
  * 			   - HSD Current High
  * Param:	none 
  * Return:  none
  * Author:  A.Feng
  *****************************************************************************/
void iniHSDDiag(HSDDiagInfo *diag,Byte lowTH,Byte highTH,Byte delayTime)
{
	diag->F_prevOutp   = 0;
	diag->F_outp = 0;
	diag->delayCounter = 0;
	diag->a2dIdx = 0;
	diag->a2dBuffer[0] = 0;
	diag->a2dBuffer[1] = 0;
	diag->a2dBuffer[2] = 0;
	diag->a2dBuffer[3] = 0;
	diag->a2dBuffer[4] = 0;
	diag->delayTime = delayTime;
	diag->errStatus = 0;
	diag->highTh = highTH;
	diag->lowTh = lowTH;
}

 void iniVBATDiag(VBATDiagInfo *diag)
{
	diag->ConfirmTimesH   = CONFIRM_VBAT;
	diag->ConfirmTimesL = CONFIRM_VBAT;
	diag->ConfirmTimesNOR = CONFIRM_VBAT;
	diag->ConfirmCounterH = 0;
	diag->ConfirmCounterL = 0;
	diag->ConfirmCounterNOR = 0;
	diag->a2dIdx = 0;
	diag->errStatusH = 0;
	diag->errStatusL = 0;
	diag->a2dBuffer[0] = 0;
	diag->a2dBuffer[1] = 0;
	diag->a2dBuffer[2] = 0;
	diag->a2dBuffer[3] = 0;
	diag->a2dBuffer[4] = 0;

}

  void iniTemperDiag(TemperDiagInfo *diag)
{
	diag->delayTimeCounter = 0;
	diag->ConfirmTimesErr   = 10;
	diag->ConfirmTimesNOR = 10;
	diag->ConfirmCounterErr = 0;
	diag->ConfirmCounterNOR = 0;
	diag->a2dIdx = 0;
	diag->errStatus = 0;
	diag->a2dBuffer[0] = 0;
	diag->a2dBuffer[1] = 0;
	diag->a2dBuffer[2] = 0;
	diag->a2dBuffer[3] = 0;
	diag->a2dBuffer[4] = 0;

}


  /*****************************************************************************/
  /**
   * Function Name: void iniLSDDiags(RelayDiagInfo *diag)
   * Description:	ini LSD diag
   * Param:  none 
   * Return:  none
   * Author:  A.Feng
   *****************************************************************************/
 void iniLSDDiag(LSDDiagInfo *diag,Byte delayTime)
 {
     diag->F_prevOutp   = 0;
     diag->F_outp = 0;
     diag->delayCounter = 0;
     diag->delayTime = delayTime;
     diag->errStatus = 0;
 }
  /****************************************************************************/
 /**
  * Function Name: diag_getDtcIndex
  * Description: none
  *
  * Param:	 none
  * Return:  none
  * Author:  2016/08/30, hui.liu create this function
  ****************************************************************************/
 UINT8 diag_getDtcIndex(UINT8 high, UINT8 middle, UINT8 low)
 {
	 UINT8 u8_index;
	 for(u8_index = 0; u8_index < NUM_OF_DTCS; u8_index++)
	 {
		 if((dtcLUT[u8_index][0] == high)
				 && (dtcLUT[u8_index][1] == middle)
				 && (dtcLUT[u8_index][2] == low)
		   )
		 {
			 break;
		 }
	 }
	 return (u8_index);
 }

